/*
 * MIT License
 *
 * Copyright (c) 2023 北京凯特伟业科技有限公司
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.je.bpm.engine.impl;

import com.je.bpm.engine.ActivitiIllegalArgumentException;
import com.je.bpm.engine.impl.context.Context;
import com.je.bpm.engine.impl.interceptor.CommandContext;
import com.je.bpm.engine.impl.interceptor.CommandExecutor;
import com.je.bpm.engine.runtime.Job;
import com.je.bpm.engine.runtime.JobQuery;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

public class JobQueryImpl extends AbstractQuery<JobQuery, Job> implements JobQuery, Serializable {

    private static final long serialVersionUID = 1L;
    protected String id;
    protected String processInstanceId;
    protected String executionId;
    protected String processDefinitionId;
    protected boolean retriesLeft;
    protected boolean executable;
    protected boolean onlyTimers;
    protected boolean onlyMessages;
    protected Date duedateHigherThan;
    protected Date duedateLowerThan;
    protected Date duedateHigherThanOrEqual;
    protected Date duedateLowerThanOrEqual;
    protected boolean withException;
    protected String exceptionMessage;
    protected String tenantId;
    protected String tenantIdLike;
    protected boolean withoutTenantId;
    protected boolean noRetriesLeft;
    protected boolean onlyLocked;
    protected boolean onlyUnlocked;
    
    public JobQueryImpl() {
    }

    public JobQueryImpl(CommandContext commandContext) {
        super(commandContext);
    }

    public JobQueryImpl(CommandExecutor commandExecutor) {
        super(commandExecutor);
    }

    @Override
    public JobQuery jobId(String jobId) {
        if (jobId == null) {
            throw new ActivitiIllegalArgumentException("Provided job id is null");
        }
        this.id = jobId;
        return this;
    }

    @Override
    public JobQueryImpl processInstanceId(String processInstanceId) {
        if (processInstanceId == null) {
            throw new ActivitiIllegalArgumentException("Provided process instance id is null");
        }
        this.processInstanceId = processInstanceId;
        return this;
    }

    @Override
    public JobQueryImpl processDefinitionId(String processDefinitionId) {
        if (processDefinitionId == null) {
            throw new ActivitiIllegalArgumentException("Provided process definition id is null");
        }
        this.processDefinitionId = processDefinitionId;
        return this;
    }

    @Override
    public JobQueryImpl executionId(String executionId) {
        if (executionId == null) {
            throw new ActivitiIllegalArgumentException("Provided execution id is null");
        }
        this.executionId = executionId;
        return this;
    }

    public JobQuery withRetriesLeft() {
        retriesLeft = true;
        return this;
    }

    public JobQuery executable() {
        executable = true;
        return this;
    }

    @Override
    public JobQuery timers() {
        if (onlyMessages) {
            throw new ActivitiIllegalArgumentException("Cannot combine onlyTimers() with onlyMessages() in the same query");
        }
        this.onlyTimers = true;
        return this;
    }

    @Override
    public JobQuery messages() {
        if (onlyTimers) {
            throw new ActivitiIllegalArgumentException("Cannot combine onlyTimers() with onlyMessages() in the same query");
        }
        this.onlyMessages = true;
        return this;
    }

    @Override
    public JobQuery duedateHigherThan(Date date) {
        if (date == null) {
            throw new ActivitiIllegalArgumentException("Provided date is null");
        }
        this.duedateHigherThan = date;
        return this;
    }

    @Override
    public JobQuery duedateLowerThan(Date date) {
        if (date == null) {
            throw new ActivitiIllegalArgumentException("Provided date is null");
        }
        this.duedateLowerThan = date;
        return this;
    }

    public JobQuery duedateHigherThen(Date date) {
        return duedateHigherThan(date);
    }

    public JobQuery duedateHigherThenOrEquals(Date date) {
        if (date == null) {
            throw new ActivitiIllegalArgumentException("Provided date is null");
        }
        this.duedateHigherThanOrEqual = date;
        return this;
    }

    public JobQuery duedateLowerThen(Date date) {
        return duedateLowerThan(date);
    }

    public JobQuery duedateLowerThenOrEquals(Date date) {
        if (date == null) {
            throw new ActivitiIllegalArgumentException("Provided date is null");
        }
        this.duedateLowerThanOrEqual = date;
        return this;
    }

    public JobQuery noRetriesLeft() {
        noRetriesLeft = true;
        return this;
    }

    @Override
    public JobQuery withException() {
        this.withException = true;
        return this;
    }

    @Override
    public JobQuery exceptionMessage(String exceptionMessage) {
        if (exceptionMessage == null) {
            throw new ActivitiIllegalArgumentException("Provided exception message is null");
        }
        this.exceptionMessage = exceptionMessage;
        return this;
    }

    @Override
    public JobQuery jobTenantId(String tenantId) {
        if (tenantId == null) {
            throw new ActivitiIllegalArgumentException("job is null");
        }
        this.tenantId = tenantId;
        return this;
    }

    @Override
    public JobQuery jobTenantIdLike(String tenantIdLike) {
        if (tenantIdLike == null) {
            throw new ActivitiIllegalArgumentException("job is null");
        }
        this.tenantIdLike = tenantIdLike;
        return this;
    }

    @Override
    public JobQuery jobWithoutTenantId() {
        this.withoutTenantId = true;
        return this;
    }

    @Override
    public JobQuery locked() {
        this.onlyLocked = true;
        return this;
    }

    @Override
    public JobQuery unlocked() {
        this.onlyUnlocked = true;
        return this;
    }

    // sorting //////////////////////////////////////////
    @Override
    public JobQuery orderByJobDuedate() {
        return orderBy(JobQueryProperty.DUEDATE);
    }

    @Override
    public JobQuery orderByExecutionId() {
        return orderBy(JobQueryProperty.EXECUTION_ID);
    }

    @Override
    public JobQuery orderByJobId() {
        return orderBy(JobQueryProperty.JOB_ID);
    }

    @Override
    public JobQuery orderByProcessInstanceId() {
        return orderBy(JobQueryProperty.PROCESS_INSTANCE_ID);
    }

    @Override
    public JobQuery orderByJobRetries() {
        return orderBy(JobQueryProperty.RETRIES);
    }

    @Override
    public JobQuery orderByTenantId() {
        return orderBy(JobQueryProperty.TENANT_ID);
    }

    // results //////////////////////////////////////////
    @Override
    public long executeCount(CommandContext commandContext) {
        checkQueryOk();
        return commandContext.getJobEntityManager().findJobCountByQueryCriteria(this);
    }

    @Override
    public List<Job> executeList(CommandContext commandContext, Page page) {
        checkQueryOk();
        return commandContext.getJobEntityManager().findJobsByQueryCriteria(this, page);
    }

    // getters //////////////////////////////////////////

    public String getProcessInstanceId() {
        return processInstanceId;
    }

    public String getExecutionId() {
        return executionId;
    }

    public boolean getRetriesLeft() {
        return retriesLeft;
    }

    public boolean getExecutable() {
        return executable;
    }

    public Date getNow() {
        return Context.getProcessEngineConfiguration().getClock().getCurrentTime();
    }

    public boolean isWithException() {
        return withException;
    }

    public String getExceptionMessage() {
        return exceptionMessage;
    }

    public String getTenantId() {
        return tenantId;
    }

    public String getTenantIdLike() {
        return tenantIdLike;
    }

    public boolean isWithoutTenantId() {
        return withoutTenantId;
    }

    public static long getSerialversionuid() {
        return serialVersionUID;
    }

    public String getId() {
        return id;
    }

    public String getProcessDefinitionId() {
        return processDefinitionId;
    }

    public boolean isOnlyTimers() {
        return onlyTimers;
    }

    public boolean isOnlyMessages() {
        return onlyMessages;
    }

    public Date getDuedateHigherThan() {
        return duedateHigherThan;
    }

    public Date getDuedateLowerThan() {
        return duedateLowerThan;
    }

    public Date getDuedateHigherThanOrEqual() {
        return duedateHigherThanOrEqual;
    }

    public Date getDuedateLowerThanOrEqual() {
        return duedateLowerThanOrEqual;
    }

    public boolean isNoRetriesLeft() {
        return noRetriesLeft;
    }

    public boolean isOnlyLocked() {
        return onlyLocked;
    }

    public boolean isOnlyUnlocked() {
        return onlyUnlocked;
    }

}
